﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Web;
using Douban.Api.Authorization;
using Lipcas.Common.FluentAspects;

namespace Douban.Api.Web
{
    public class WebChannel
    {
        private const int DefaultTimeout = 60000;

        public AuthorizedResponse Send(AuthorizedRequest request)
        {
            VerifyRequest(request);

            HttpWebRequest httpRequest =
                            request.HttpMethod == HttpMethods.Get ?
                                GetGetRequest(request) :
                                GetNonGetRequest(request);
            httpRequest.Timeout = DefaultTimeout;

            return new AuthorizedResponse((HttpWebResponse)httpRequest.GetResponse());
        }

        public HttpWebRequest GetGetRequest(AuthorizedRequest request)
        {
            var commonQueryParameters = GetCommonQueryParameters(request);
            string url = NormalizeUrl(
                request.RequestUri.BasePath(), commonQueryParameters.ToString());

            if (IsProtectedResource(request))
            {
                OAuth auth = new OAuth();
                url = auth.GenerateRequestUrl(
                    new Uri(url),
                    request.ConsumerKey,
                    request.ConsumerSecret,
                    request.AccessToken,
                    request.AccessTokenSecret,
                    request.HttpMethod.ToString(),
                    auth.GenerateTimeStamp(),
                    auth.GenerateNonce());
            }

            var httpRequest = (HttpWebRequest)WebRequest.Create(url);
            httpRequest.Method = "get";

            return httpRequest;
        }

        public HttpWebRequest GetNonGetRequest(AuthorizedRequest request)
        {
            string normalizedUri = null;
            string normalizedParamters = null;

            OAuth auth = new OAuth();
            string signature = auth.GenerateSignature(
                new Uri(request.RequestUri.BasePath()),
                request.ConsumerKey,
                request.ConsumerSecret,
                request.AccessToken,
                request.AccessTokenSecret,
                request.HttpMethod.ToString(),
                auth.GenerateTimeStamp(),
                auth.GenerateNonce(),
                out normalizedUri,
                out normalizedParamters);

            HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(normalizedUri);
            httpRequest.Method = request.HttpMethod.ToString();
            httpRequest.ContentType = "application/atom+xml";       

            QueryParameterCollection queryParameters = QueryParameterCollection.Resolve(normalizedParamters);
            queryParameters.Add("oauth_signature", signature);

            WebHeaderCollection headers = httpRequest.Headers;
            headers.Add("Authorization", queryParameters.ToString(", "));
         
            if (request.Data != null)
            {
                UTF8Encoding encoding = new UTF8Encoding();
                byte[] queryBytes = encoding.GetBytes(request.Data);
                httpRequest.ContentLength = queryBytes.Length;
                httpRequest.GetRequestStream().Write(queryBytes, 0, queryBytes.Length);
            }

            return httpRequest;
        }

        private QueryParameterCollection GetCommonQueryParameters(AuthorizedRequest request)
        {
            QueryParameterCollection commonQueryParameters = 
                QueryParameterCollection.Resolve(request.RequestUri.Query);

            commonQueryParameters.Add("apikey", request.ConsumerKey);

            if(request.StartIndex > 0)
                commonQueryParameters.Add("start-index", request.StartIndex.ToString());

            if(request.MaxResults > 0)
                commonQueryParameters.Add("max-results", request.MaxResults.ToString());

            return commonQueryParameters;
        }

        private string NormalizeUrl(string url, string parameters)
        {
            int markIndex = url.IndexOf("?");

            if (markIndex >= 0)
                url += string.Format("&{0}", parameters);
            else
                url += string.Format("?{0}", parameters);

            return url;
        }

        public bool IsProtectedResource(AuthorizedRequest request)
        {
            return !string.IsNullOrEmpty(request.AccessToken) &&
                   !string.IsNullOrEmpty(request.AccessTokenSecret);
        }

        public void VerifyRequest(AuthorizedRequest request)
        {
            if (string.IsNullOrEmpty(request.ConsumerKey))
                throw new MissingConsumerKeyException();

            if (!IsProtectedResource(request)) return;

            if (string.IsNullOrEmpty(request.ConsumerSecret))
                throw new MissingComsumerSecretException();
        }
    }
}
